﻿using DG.Tweening;
using DG.Tweening.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Profiling;

using Object = UnityEngine.Object;

namespace PP.Basic
{
    /// <summary>
    /// Scheduler, 计时任务对象
    /// </summary>
    public class Scheduler : SingletonUnsafe<Scheduler>
    {
        private const string Tag = nameof(Scheduler);

        // Public Variables Begin
        /// <summary>
        /// 错误处理事件
        /// </summary>
        public event Action<string /*objectName*/, string /*methodName*/, string /*ExceptionMessage*/> ErrorHandling;
        // Public Variables End

        // Private Variables Begin
        /// <summary>
        /// 公用的空Setter，避免重复创建
        /// </summary>
        private static DOSetter<float> _emptySetter = (value) => { };
        // Private Variables End

        // Public Methods Begin
        /// <summary>
        /// 计划一个时间任务回调，并返回该计时任务对象
        /// </summary>
        /// <param name="interval">时间间隔</param>
        /// <param name="handler">回调方法</param>
        /// <param name="repeat">重复次数，-1: 无限循环</param>
        /// <param name="targetObject">        
        /// 绑定目标对象，可以是 GameObject、Transform、Behaviour、Component、任意脚本对象。
        /// 若目标对象为 null，则为全局回调；若目标对象不为 null，则当它被销毁时，自动取消计划任务
        /// </param>
        /// <param name="autoKillOnCompletion">是否在完成时自动销毁，默认true</param>
        /// <returns>Tween task， 计划对象</returns>
        public Tween Schedule(float interval, Action handler, int repeat = -1, Object targetObject = null, bool autoKillOnCompletion = true)
        {
            interval = Mathf.Max(0f, interval);
            repeat = Mathf.Max(-1, repeat);

            Tween task = null;
            task = DOTween.To(() => 0f, _emptySetter, 1, interval)
            .OnStepComplete(() =>
            {
                // 无目标对象或目标对象依然存活，则执行回调
                var ok = CanInvoke(targetObject); // isNull || isAlive                
                Debug.Log($"Schedule :ok={ok}");
                if (ok)
                {
                    try
                    {
                        handler?.Invoke();
                    }
                    catch (Exception ex)
                    {
                        Debug.LogException(new Exception($"{Tag}.ScheduleERROR_targetObject:{targetObject?.name}_handlerName:{handler.Method.Name}_Exception: {ex}"));
                        OnHandlerInvokeError(handler, targetObject, ex);
                    }
                }

                // 目标对象已销毁，停止执行
                if (!ok)
                {
                    // Debug.Log($"{Tag}.Schedule# handlerName: Object is destroyed. Handler: {handler.Method.Name}.");
                    Unschedule(task);
                }
            })
            .SetLoops(repeat)
            .SetAutoKill(autoKillOnCompletion)
            .SetTarget(targetObject);

            return task;
        }


        /// <summary>
        /// 延时调用
        /// </summary>
        /// <param name="delay">延时时间</param>
        /// <param name="handler">回调方法</param>
        /// <param name="targetObject">目标对象</param>
        /// <param name="targetObject">
        /// 绑定目标对象，可以是 GameObject、Transform、Behaviour、Component、任意脚本对象。
        /// 若目标对象为 null，则为全局回调；若目标对象不为 null，则当它被销毁时，自动取消计划任务
        /// </param>
        /// <param name="autoKillOnCompletion">是否在完成时自动销毁，默认true</param>
        public Tween DelayedCall(float delay, Action handler, Object targetObject = null, bool autoKillOnCompletion = true)
        {
            return Schedule(delay, handler, 1, targetObject, autoKillOnCompletion);
        }



        /// <summary>
        /// 取消此计划
        /// </summary>
        /// <param name="task"></param>
        public void Unschedule(Tween task)
        {
            if (task != null && task.IsActive())
                task.Kill();
        }

        /// <summary>
        /// 取消绑定在目标对象上的计划
        /// </summary>
        /// <param name="targetObject"></param>
        public void Unschedule(Object targetObject)
        {
            DOTween.Kill(targetObject);
        }



        /// <summary>
        /// 根据targetObject绑定对象，判断是否可以执行调用
        /// </summary>
        /// <param name="targetObject"></param>
        /// <returns></returns>
        public bool CanInvoke(Object targetObject)
        {
            // 无绑定对象，可以执行调用
            if ((object)targetObject == null)
                return true;

            var ok = false;
            do
            {
                // 绑定对象被Destroy，则不可执行调用
                if (!targetObject)
                    break;

                // 绑定对象依然有效，可以执行调用
                ok = true;
            } while (false);
            return ok;
        }
        // Public Methods End

        // Event Methods Begin
        private void OnHandlerInvokeError(Action handler, Object targetObject, Exception exception)
        {
            try
            {
                // 触发事件
                ErrorHandling?.Invoke(handler.Method.Name, targetObject?.name, exception.Message);
            }
            catch (Exception ex)
            {
                Debug.LogError($"{Tag}.OnHandlerInvokeError# Exception: {ex}");
            }            
        }
        // Event Methods End

    }
}
